using System;

namespace Drill.Extensibility
{
	/// <summary>
	/// Base implementation of <see cref="IDrillBitSetup"/> 
	/// that may be inherited by classes as a convenience to a full implementation.
	/// </summary>
	/// <typeparam name="TDrillBit">The type of the DrillBit.</typeparam>
	/// <typeparam name="TResolverObject">The type of the resolver object (e.g. container).</typeparam>
	public abstract class DrillBitSetupBase<TDrillBit, TResolverObject>
		: IDrillBitSetup
		where TDrillBit : class, IDrillBit
		where TResolverObject : class
	{
		/// <summary>
		/// Configures the specified resolver object.
		/// </summary>
		/// <param name="resolverObject">The resolover object (container) to be configured.</param>
		protected abstract void Configure(TResolverObject resolverObject);

		/// <summary>
		/// Configures the specified DrillBit.
		/// </summary>
		/// <param name="drillBit">The DrillBit to be configured.</param>
		/// <param name="state">An instance of TResolverObject.</param>
		/// <exception cref="System.ArgumentNullException">drillBit</exception>
		/// <exception cref="System.ArgumentException">drillBit</exception>
		/// <exception cref="System.ArgumentException">state</exception>
		public virtual void Configure(IDrillBit drillBit, object state)
		{
			if (null == drillBit)
			{
				throw new ArgumentNullException("drillBit");
			}

			if (false == drillBit is TDrillBit)
			{
				throw new ArgumentException(
					string.Format(
						"The drillBit parameter must be an instance of the '{0}' class."
					  , typeof(TDrillBit).FullName
						)
				  , "drillBit"
					);
			}

			// When there is an explicit state object available,
			// it must be used as the resolver object.
			//
			// When no explicit state object is available, 
			// obtain the resolver object from the DrillBit.

			TResolverObject resolverObject;

			if (null != state)
			{
				if (false == state is TResolverObject)
				{
					throw new ArgumentException(
						string.Format(
							"The state parameter must be an instance of the '{0}' class."
						  , typeof(TResolverObject).FullName
							)
					  , "state"
						);
				}

				resolverObject = (TResolverObject)state;
			}
			else
			{
				resolverObject = (TResolverObject)drillBit.ResolverObject;
			}

			Configure(resolverObject);
		}
	}
}